/*
This file is part of Docking@Home.
http://docking.gcl.cis.udel.edu
Copyright (C) 2009 University of Delaware

Docking@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Docking@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Docking@Home.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
	Filename: Resources.cpp
	Description: Part of the Low Level Tab that displays information
		about the current resource in the right half of the tab
		window
	Revision History: 
		Created - 01/21/08
	Author: Robert Keller (01/21/08 -> Current)
*/
#include "Resources.h"

#include "parse.h"
#include "gutil.h"
#include "boinc_gl.h"
#include "graphics_api.h"
#include "txf_util.h"

#include <sys/types.h> 
#include <sys/stat.h> 

// ------------------------------------------------------------
// Static Variable Declarations
// ------------------------------------------------------------

GLHelper * Resources::glHelper;

const char * Resources::PERCENT_FILE = "percentdone.txt";
const char * Resources::SUMMARY_FILE = "summary.txt";

// ------------------------------------------------------------
// Constructor / Destructor
// ------------------------------------------------------------

Resources::Resources ()
	: OBJECT_PADDING(5.0f),
	  MAX_GRAPH_POINTS(21),
	  percentageTitle((char*)"Percentage Complete:"),
	  userTitle((char*)"User: "),
	  groupTitle((char*)"Group: "),
	  minEnerTitle((char*)"Min Energy: "),
	  rmsdTitle((char*)"RMSD: ")
{
	init (0.0f, 0.0f, 0.0f);
}// Resources

Resources::Resources (float r, float h, float ls)
	: OBJECT_PADDING(5.0f),
	  MAX_GRAPH_POINTS(21),
	  percentageTitle((char*)"Percentage Complete:"),
	  userTitle((char*)"User: "),
	  groupTitle((char*)"Group: "),
	  minEnerTitle((char*)"Min Energy: "),
	  rmsdTitle((char*)"RMSD: ")
{
	init (r, h, ls);
}// Resources

Resources::~Resources ()
{
}// ~Resources

// ------------------------------------------------------------
// Initialization Functions
// ------------------------------------------------------------

void Resources::init (float r, float h, float ls)
{
	textXScale = .75f;
	textYScale = -0.75f;
	textZScale = 1.0f;

	cylinderRotAxis[0] = 1.0f;
	cylinderRotAxis[1] = 0.0f;
	cylinderRotAxis[2] = 0.0f;

	cylinderRadius = r;
	cylinderHeight = h;

	minEnerGraphWidth = 75.0f;
	minEnerGraphHeight = 75.0f;

	rmsdGraphWidth = 75.0f;
	rmsdGraphHeight = 75.0f;

	angle = 90;

	lineSpacing = ls;

	// Array Initialization 

	userTitlePos[0] = 0.0f;
	userTitlePos[1] = 0.0f;
	userTitlePos[2] = 0.0f;

	groupTitlePos[0] = 0.0f;
	groupTitlePos[1] = 0.0f;
	groupTitlePos[2] = 0.0f;

	cylinderPos[0] = 0.0f;
	cylinderPos[1] = 0.0f;
	cylinderPos[2] = 0.0f;

	percentageTitlePos[0] = 0.0f;
	percentageTitlePos[1] = 0.0f;
	percentageTitlePos[2] = 0.0f;

	minEnerTitlePos[0] = 0.0f;
	minEnerTitlePos[1] = 0.0f;
	minEnerTitlePos[2] = 0.0f;

	minEnerGraphPos[0] = 0.0f;
	minEnerGraphPos[1] = 0.0f;
	minEnerGraphPos[2] = 0.0f;

	minEnerGraphXMin = 0.0f;
	minEnerGraphYMin = 0.0f;

	minEnerGraphXMax = 0.0f;
	minEnerGraphYMax = 0.0f;

	rmsdTitlePos[0] = 0.0f;
	rmsdTitlePos[1] = 0.0f;
	rmsdTitlePos[2] = 0.0f;

	rmsdGraphPos[0] = 0.0f;
	rmsdGraphPos[1] = 0.0f;
	rmsdGraphPos[2] = 0.0f;

	rmsdGraphXMin = 0.0f;
	rmsdGraphYMin = 0.0f;

	rmsdGraphXMax = 0.0f;
	rmsdGraphYMax = 0.0f;

	summaryLastModified=0;
	percentLastModified=0;
	percentDone=0;

}// init

// ------------------------------------------------------------
// Member Modifiers 
// ------------------------------------------------------------

void Resources::setCylinderRadius (float r)
{
	cylinderRadius = r;
}// setCylinderRadius 

void Resources::setCylinderHeight (float h)
{
	cylinderHeight = h;
}// setCylinderHeight 

void Resources::setLineSpacing (float ls)
{
	lineSpacing = ls;
}// setLineSpacing

void Resources::setUserTitlePos (float x, float y, float z)
{
	userTitlePos[0] = x;
	userTitlePos[1] = y;
	userTitlePos[2] = z;
}// setUserTitlePos

void Resources::setGroupTitlePos (float x, float y, float z)
{
	groupTitlePos[0] = x;
	groupTitlePos[1] = y;
	groupTitlePos[2] = z;
}// setGroupTitlePos

void Resources::setCylinderPos (float x, float y, float z)
{
	cylinderPos[0] = x;
	cylinderPos[1] = y;
	cylinderPos[2] = z;
}// setCylinderPos

void Resources::setPercentageTitlePos (float x, float y, float z)
{
	percentageTitlePos[0] = x;
	percentageTitlePos[1] = y;
	percentageTitlePos[2] = z;
}// setPercentageTitlePos

void Resources::setUserName (const char * uname)
{
	memcpy(userName, uname, sizeof(char) * RESOURCES_MAX_LENGTH);
}// setUserName

void Resources::setGroupName (const char * gname)
{
	memcpy(groupName, gname, sizeof(char) * RESOURCES_MAX_LENGTH);
}// setUserName

void Resources::setTextColor (const float color[4])
{
	glHelper->copyColor4f(textColor, color);
}// setCylinderFullColor

void Resources::setCylinderFullColor (const float color[4])
{
	glHelper->copyColor4f(cylinderFullColor, color);
}// setCylinderFullColor

void Resources::setCylinderEmptyColor (const float color[4])
{
	glHelper->copyColor4f(cylinderEmptyColor, color);
}// setCylinderEmptyColor

// ------------------------------------------------------------
// Update Data
// ------------------------------------------------------------
//gets numbers from a string
double ToNumber(string number){
	string keep="";
	bool decimal=false;
	bool negation=false;
	//for each char in the string
	for(size_t i=0;i<number.size();i++){
		//if we find 0-9
		if(isdigit(number[i])){
			keep+=number[i];
		}//if we find a decimal point
		else if(!decimal&&number[i]=='.'&&i+1<number.size()&&isdigit(number[i+1])){
			keep+='.';
			decimal=true;
		}//if we find a negation sign
		else if(!negation&&number[i]=='-'&&i+1<number.size()&&isdigit(number[i+1])){
			keep+='-';
			negation=true;
		}//we already found a number and we've reached it's end
		else if(keep.size()){
			break;
		}
	}
	//return the numer
	return atof(keep.c_str());
}
//get a line from a file
string GetLine(FILE *file){
	string line;
	//check if the file's open
	if(file){
		char buf[64];		
		size_t pos=line.npos;
		//get the buffer from file
		while(!feof(file)&&pos==line.npos){
			//get some stuff
			if(fgets(buf,64,file)==NULL)
				break;
			//add it to the string
			line+=buf;
			//did we get the endline yet?
			pos=line.find('\n');
		}
	}
	//return what we have
	return line;
}
//split a string into elements depending on where the pattern is in string
vector<string> Split(string phrase,string pattern,bool keepPattern){
	//vector to hold the parts
	vector<string> parts;
	//shortcircuit
	if(phrase=="")
		return parts;
	//invalid position
	size_t pos=phrase.find(pattern);
	//while we have parts to split by
	while(pos!=phrase.npos){
		//take the up to the pattern
		parts.push_back(phrase.substr(0,pos+(keepPattern?pattern.size():0)));
		//remove it
		phrase=phrase.substr(pos+pattern.size());
		//do we have anymore
		pos=phrase.find(pattern);
		//if we added a blank string
		if(!parts.back().compare("")){
			//remove it
			parts.pop_back();
		}
	}
	//add the rest on
	parts.push_back(phrase);
	//return it
	return parts;
}
//for sorting
bool Point2fTLessThan(Point2fT point1,Point2fT point2){
	return point1.s.Y>point2.s.Y;
}

//we go to file here to get facts about what boinc app is crunching and its progress
//we then save those details in shared memory so that the screen saver can get them
void Resources::PercentDone(const char *filename,string *keys,double *data,size_t size) {
	double done=0;
	//open the percentdone file
	FILE *f = fopen(filename, "r");
	//if we found the file to open
	if(f){
		string info;
		size_t pos=info.npos;
		//get the buffer from file
		while(!feof(f)){
			//get a line from file
			info=GetLine(f);
			//for each key
			for(size_t i=0;i<size;i++){
				//check if this line has it
				pos=info.find(keys[i].c_str());
				//if we found the key
				if(pos!=info.npos){
					//get the number portion
					pos+=keys[i].size();
					data[i]=ToNumber(info.substr(pos,info.size()-pos));
					//we are done with this line
					break;
				}
			}
		}		
		//close the file
		fclose(f);
	}//the file didn't exist
	else{
		fprintf(stderr, "Can't find file: %s\n", filename);
	}
}
//we go to file here to get a list of progress with the minenergy and minrmsd
void Resources::Summary(const char *filename){
	bool found=false;
	vector<string> line;
	Point2fT point;
	//open the percentdone file
	FILE *f = fopen(filename, "r");
	//if we found the file to open
	if(f){
		string info;
		size_t pos=info.npos;
		//get the buffer from file
		while(!feof(f)){
			//get a line from file
			info=GetLine(f);
			//split the line
			line=Split(info," ",false);
			//did we get anything
			if(line.size()){
				//if it's not a number skip it (because atof returns 0.0 when it can't parse)
				if(ToNumber(line[0])==0.0&&line[0].compare("0.0")){
					continue;
				}//we need to look at this line, it's an error if it's a 1001
				else if(ToNumber(line[0])!=1001.0){
					if(!found){
						//forget the old points
						this->rmsdGraphPoints.clear();
						this->minEnerGraphPoints.clear();
					}
					found=true;					
					//add the new points
					point.s.Y=(float)ToNumber(line[0]);
					this->rmsdGraphPoints.push_back(point);
					point.s.Y=(float)ToNumber(line[1]);
					this->minEnerGraphPoints.push_back(point);
				}
			}
		}
		//close the file
		fclose(f);
		//make the graphs look nice
		this->FixMinEnerPoints();
		this->FixRMSDPoints();
	}//the file didn't exist
	else{
		fprintf(stderr, "Can't find file: %s\n", filename);
	}
}
void Resources::updateData(){

	//FOR THE PERCENT DONE
	struct stat fileInfo;
	stat(PERCENT_FILE, &fileInfo);
	//see if we need to check the file
	if (percentLastModified != fileInfo.st_mtime){
		//update when the file was last modified
		percentLastModified = fileInfo.st_mtime;
		//go to file to get the progress info
		string keys[3]={"FDONE","MAXENER","MAXRMSD"};
		double data[3];	memset(data,0,sizeof(data));
		PercentDone(PERCENT_FILE,keys,data,1);
		percentDone=data[0]*.01;
		boinc_fraction_done(percentDone);
	}

	//FOR THE GRAPHS
	if(!boinc_resolve_filename(SUMMARY_FILE, resolved, sizeof(resolved))){
		stat(resolved, &fileInfo);
		//see if we need to check the file
		if (summaryLastModified != fileInfo.st_mtime){
			//update when the file was last modified
			summaryLastModified = fileInfo.st_mtime;
			//update the vectors that the graph uses
			Summary(resolved);
		}
	}

}// updateData

// ------------------------------------------------------------
// Window Size Adjusting Functions 
// ------------------------------------------------------------

void Resources::resize (float topLeftX, float topLeftY, 
	float rightX, float xFactor, float yFactor, bool bFullscreen)
{
	float currLineSpacing;

	// Resize the elements and their positions based on whether it is
	// in fullscreen mode or not
	if (bFullscreen)
	{		
		// Change text and line scales
		currLineSpacing = lineSpacing * xFactor * .7f;
		
		currTextXScale = textXScale * xFactor * .7f;
		currTextYScale = textYScale * yFactor * .7f;
		currTextZScale = textZScale;

		// Update progress cylinder
		currCylinderRadius = cylinderRadius * xFactor * .7f;
		currCylinderHeight = cylinderHeight * yFactor * .7f;

		// Update graph sizes
		currMinEnerGraphWidth = minEnerGraphWidth * xFactor * .75f;
		currMinEnerGraphHeight = minEnerGraphHeight * yFactor * .75f;

		currRMSDGraphWidth = rmsdGraphWidth * xFactor * .75f;
		currRMSDGraphHeight = rmsdGraphHeight * yFactor * .75f;

		// Update positions
		userTitlePos[0] = 0.0f;
		userTitlePos[1] = topLeftY - currLineSpacing * .3f;

		groupTitlePos[0] = 0.0f;
		groupTitlePos[1] = userTitlePos[1] + currLineSpacing;

		percentageTitlePos[0] = 0.0f;
		percentageTitlePos[1] = groupTitlePos[1] + currLineSpacing;

		cylinderPos[0] = 75.0f * xFactor;
		cylinderPos[1] = percentageTitlePos[1] + currCylinderHeight 
			+ OBJECT_PADDING * 2.0f;
		
		minEnerTitlePos[0] = 0.0f;
		minEnerTitlePos[1] = cylinderPos[1] + currLineSpacing 
			+ OBJECT_PADDING;

		minEnerGraphPos[0] = xFactor * 20.0f;
		minEnerGraphPos[1] = minEnerTitlePos[1] + OBJECT_PADDING 
			+ lineSpacing * yFactor * .30f + currMinEnerGraphHeight;

		rmsdTitlePos[0] = 0.0f;
		rmsdTitlePos[1] = minEnerGraphPos[1] + OBJECT_PADDING +
			lineSpacing * yFactor;

		rmsdGraphPos[0] = xFactor * 20.0f;
		rmsdGraphPos[1] = rmsdTitlePos[1] + OBJECT_PADDING 
			+ lineSpacing * yFactor * .30f + currMinEnerGraphHeight;
	}// if
	else
	{
		// Change text scale
		currTextXScale = textXScale * xFactor;
		currTextYScale = textYScale * yFactor;
		currTextZScale = textZScale;

		// Update progress cylinder sizing
		currCylinderRadius = cylinderRadius * xFactor;
		currCylinderHeight = cylinderHeight * yFactor;

		// Update graph sizes
		currMinEnerGraphWidth = minEnerGraphWidth * xFactor;
		currMinEnerGraphHeight = minEnerGraphHeight * yFactor;

		currRMSDGraphWidth = rmsdGraphWidth * xFactor;
		currRMSDGraphHeight = rmsdGraphHeight * yFactor;

		// Update positions
		userTitlePos[0] = topLeftX;
		userTitlePos[1] = topLeftY + lineSpacing * yFactor;

		groupTitlePos[0] = topLeftX;
		groupTitlePos[1] = userTitlePos[1] + 
			lineSpacing * yFactor;

		percentageTitlePos[0] = topLeftX;
		percentageTitlePos[1] = groupTitlePos[1] + 
			lineSpacing * yFactor;

		cylinderPos[0] = topLeftX + (rightX - topLeftX) / 2.0f;
		cylinderPos[1] = percentageTitlePos[1] + currCylinderHeight 
			+ OBJECT_PADDING;

		minEnerTitlePos[0] = topLeftX;
		minEnerTitlePos[1] = cylinderPos[1] + OBJECT_PADDING +
			lineSpacing * yFactor;

		minEnerGraphPos[0] = topLeftX + xFactor * 20.0f;
		minEnerGraphPos[1] = minEnerTitlePos[1] + OBJECT_PADDING 
			+ lineSpacing * yFactor * .30f + currMinEnerGraphHeight;

		rmsdTitlePos[0] = topLeftX + (rightX - topLeftX) / 2.0f;
		rmsdTitlePos[1] = minEnerTitlePos[1];

		rmsdGraphPos[0] = topLeftX + xFactor * 20.0f 
			+ (rightX - topLeftX) / 2.0f;
		rmsdGraphPos[1] = minEnerGraphPos[1];
	}// else

}// resize

// ------------------------------------------------------------
// Rendering Functions
// ------------------------------------------------------------

void Resources::draw (double fracDone, bool bFullscreen)
{
	glHelper->setOrthographic();

	drawUserInfo ();
	drawMinEnergy ();
	drawRMSD ();
	drawPercentage(fracDone);

	glHelper->setOrthographicDone();
}// draw

void Resources::drawUserInfo ()
{
	// Temp string used for concatenation of user
	// and group information
	char str[75];

	strcpy(str, userTitle);
	strcat(str, userName);

	glHelper->drawText(0.1f, 
		userTitlePos[0], 
		userTitlePos[1],
		userTitlePos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		str);

	strcpy(str, groupTitle);
	
	if (strcmp(groupName, ""))
		strcat(str, groupName);
	else
		strcat(str, "None");

	glHelper->drawText(0.1f, 
		groupTitlePos[0], 
		groupTitlePos[1],
		groupTitlePos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		str);


}// drawUserInfo

void Resources::drawPercentage (double fracDone)
{
	char percentStr[5];

	float percentStrPos[3];

	// Set positioning
	percentStrPos[0] = cylinderPos[0] -
		(cylinderPos[0] - percentageTitlePos[0]) / 1.5f;
	percentStrPos[1] = cylinderPos[1] - 
		(cylinderPos[1] - percentageTitlePos[1]) / 5.0f;
	percentStrPos[2] = 1.0f;

	if (fracDone < 0.0)
		fracDone = 0.0;
	else if (fracDone > 1.0)
		fracDone = 1.0;

	// Draw title
	glHelper->drawText(0.1f, 
		percentageTitlePos[0], 
		percentageTitlePos[1],
		percentageTitlePos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		percentageTitle);

	// Create percentage string
	int len = sprintf (percentStr, "%2.0f", fracDone * 100);
	percentStr[len] = '%';
	percentStr[len+1] = '\0';

	// Draw percentage
	glHelper->drawText(0.1f, 
		percentStrPos[0], 
		percentStrPos[1],
		percentStrPos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		percentStr);
		
	// Draw percentage cylinder

	mode_shaded(cylinderFullColor);

	glHelper->drawCylinder(cylinderPos, 
		(float)(currCylinderHeight * fracDone), 
		currCylinderRadius, angle, 
		cylinderRotAxis);

	mode_shaded(cylinderEmptyColor);

	glHelper->drawCylinder(
		cylinderPos[0],
		cylinderPos[1] - currCylinderHeight * (float)(fracDone),
		cylinderPos[2], 
		(float)(currCylinderHeight * (1.0 - fracDone)), 
		currCylinderRadius, angle, 
		cylinderRotAxis);

	mode_unshaded();

}// drawPercentage

void Resources::drawMinEnergy ()
{
	glHelper->drawText(0.1f, 
		minEnerTitlePos[0], 
		minEnerTitlePos[1],
		minEnerTitlePos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		minEnerTitle);	

	glHelper->drawLineGraph(minEnerGraphPos[0], 
		minEnerGraphPos[1],
		currMinEnerGraphWidth, 
		currMinEnerGraphHeight,
		minEnerGraphXMin, minEnerGraphYMin,
		minEnerGraphXMax, minEnerGraphYMax,
		currTextXScale / 2.0f, currTextYScale / 2.0f,
		minEnerGraphPoints,
		textColor,
		true);

}// drawMinEnergy

void Resources::drawRMSD ()
{
	glHelper->drawText(0.1f, 
		rmsdTitlePos[0], 
		rmsdTitlePos[1],
		rmsdTitlePos[2],
		currTextXScale, currTextYScale, currTextZScale,
		textColor, 0,
		rmsdTitle);	


	glHelper->drawLineGraph(rmsdGraphPos[0], 
		rmsdGraphPos[1],
		currRMSDGraphWidth, 
		currRMSDGraphHeight,
		rmsdGraphXMin, rmsdGraphYMin,
		rmsdGraphXMax, rmsdGraphYMax,
		currTextXScale / 2.0f, currTextYScale / 2.0f,
		rmsdGraphPoints,
		textColor,
		true);

}// drawMinEnergy

// ------------------------------------------------------------
// Minimum Energy Graph Modifiers
// ------------------------------------------------------------

void Resources::FixMinEnerPoints()
{
	//bail out if there is nothing to do
	if(!this->minEnerGraphPoints.size())
		return;

	//sort the points
	std::sort(this->minEnerGraphPoints.begin(),this->minEnerGraphPoints.end(),Point2fTLessThan);

	//for each point
	size_t i=0;
	while (i!=minEnerGraphPoints.size()){
		//if we have too many data points for our graph forget the oldest one
		if(minEnerGraphPoints.size() > MAX_GRAPH_POINTS){
			minEnerGraphPoints.erase(minEnerGraphPoints.begin());
			continue;
		}//we just need to fix the x
		else{
			minEnerGraphPoints[i].s.X=(float)i;
			i++;
		}
	}

	//get the bounds
	minEnerGraphXMin = minEnerGraphPoints.front().s.X;
	minEnerGraphXMax = minEnerGraphPoints.back().s.X;
	minEnerGraphYMin = minEnerGraphPoints.back().s.Y;
	minEnerGraphYMax = minEnerGraphPoints.front().s.Y;

	//now increase the bounds to make it look nice
	//float xoffset=(this->minEnerGraphXMax-this->minEnerGraphXMin)*.1f;
	float yoffset=(this->minEnerGraphYMax-this->minEnerGraphYMin)*.1f;
	//if(xoffset==0) xoffset=1;
	//else if(xoffset>-1&&xoffset<1) xoffset=(xoffset<0?-1.f:1.f);
	if(yoffset==0) yoffset=1;
	else if(yoffset>-1&&yoffset<1) yoffset=(yoffset<0?-1.f:1.f);
	//this->minEnerGraphXMax+=xoffset;
	//this->minEnerGraphXMin-=xoffset;
	this->minEnerGraphYMax+=yoffset;
	this->minEnerGraphYMin-=yoffset;
}

// ------------------------------------------------------------
// Minimum Energy Graph Accessors
// ------------------------------------------------------------

Point2fT Resources::getMinEnerGraphPoint (unsigned int index)
{
	return minEnerGraphPoints[index];
}// getMinEnerGraphPoint

unsigned int Resources::getNumMinEnerGraphPoints ()
{
	return minEnerGraphPoints.size();
}// getNumMinEnerGraphPoints

// ------------------------------------------------------------
// RMSD Graph Modifiers
// ------------------------------------------------------------

void Resources::FixRMSDPoints ()
{
	//bail out if there is nothing to do
	if(!this->rmsdGraphPoints.size())
		return;

	//sort the points
	std::sort(this->rmsdGraphPoints.begin(),this->rmsdGraphPoints.end(),Point2fTLessThan);

	//for each point
	size_t i=0;
	while (i!=rmsdGraphPoints.size()){
		//if we have too many data points for our graph forget the oldest one
		if(rmsdGraphPoints.size() > MAX_GRAPH_POINTS){
			rmsdGraphPoints.erase(rmsdGraphPoints.begin());
			continue;
		}//we just need to fix the x
		else{
			rmsdGraphPoints[i].s.X=(float)i;
			i++;
		}
	}

	//get the bounds
	rmsdGraphXMin = rmsdGraphPoints.front().s.X;
	rmsdGraphXMax = rmsdGraphPoints.back().s.X;
	rmsdGraphYMin = rmsdGraphPoints.back().s.Y;
	rmsdGraphYMax = rmsdGraphPoints.front().s.Y;

	//now increase the bounds to make it look nice
	//float xoffset=(this->rmsdGraphXMax-this->rmsdGraphXMin)*.1f;
	float yoffset=(this->rmsdGraphYMax-this->rmsdGraphYMin)*.1f;
	//if(xoffset==0) xoffset=1;
	//else if(xoffset>-1&&xoffset<1) xoffset=(xoffset<0?-1.f:1.f);
	if(yoffset==0) yoffset=1;
	else if(yoffset>-1&&yoffset<1) yoffset=(yoffset<0?-1.f:1.f);
	//this->rmsdGraphXMax+=xoffset;
	//this->rmsdGraphXMin-=xoffset;
	this->rmsdGraphYMax+=yoffset;
	this->rmsdGraphYMin-=yoffset;
}

// ------------------------------------------------------------
// RMSD Graph Accessors
// ------------------------------------------------------------

Point2fT Resources::getRMSDGraphPoint (unsigned int index)
{
	return rmsdGraphPoints[index];
}// getMinEnerGraphPoint

unsigned int Resources::getNumRMSDGraphPoints ()
{
	return rmsdGraphPoints.size();
}// getNumRMSDGraphPoints